<!--
 * @file 高德地图
 * @author its-wild(https://gitee.com/its-wild)
 * @date 2021/08/07
-->
<template>
  <div class="a-map">
    <div class="a-map-action">
      <div class="a-map-search">
        <input
          class="a-map-search-input"
          placeholder="请输入关键词"
          @input="handleInput"
        />
        <ul
          v-show="showSearchRes"
          class="a-map-tips"
        >
          <template
            v-for="(tip, i) in tips"
            :key="i.toString()"
          >
            <li
              class="etc"
              :title="tip.name + '  ' + tip.district"
              @click="selectAddr(tip)"
              v-if="tip.location && tip.location.length > 0"
            >
              <strong>{{ tip.name }}</strong>
              <span>{{ tip.district }}</span>
            </li>
          </template>
        </ul>
      </div>
      <div
        v-show="addr"
        class="posi"
      >
        <span>经度: {{ lngNum }}，纬度: {{ latNum }}</span>
        <span style="margin-left: 20px"> 详细地址: {{ addr }} </span>
      </div>
    </div>

    <div
      id="a-map-container"
      :style="{ height: mapHeight }"
    ></div>
  </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import AMapLoader from '@amap/amap-jsapi-loader'
import axios from 'axios'
// 输入防抖定时器
let timer: any = -1
const searchUrl = 'https://restapi.amap.com/v3/assistant/inputtips'
let setPositionByLnglat: Function = () => {}
const props = defineProps({
  mapKey: {
    type: String,
    required: true
  },
  modelValue: {
    type: Array,
    default () {
      return []
    }
  },
  mapHeight: {
    type: String,
    default: ''
  }
})

const emit = defineEmits(['update:modelValue', 'onSeleted'])
let keywords = ''
const tips = ref<any>([])
const showSearchRes = ref(false)
const initLnglat: any = props.modelValue && props.modelValue.length === 2 ? props.modelValue : null
const lngNum = ref()
const latNum = ref()
const addr = ref('')
const updateLngNum = (val: number) => {
  lngNum.value = val
}
const updateLatNum = (val: number) => {
  latNum.value = val
}
const updateAddr = (val: string) => {
  addr.value = val
}

const loadAMap = (key: string, center?: number[]) => {
  // 加载地图的配置
  const loadOption = {
    key, // 申请好的Web端开发者Key，首次调用 load 时必填
    version: '1.4.16', // 指定要加载的 JSAPI 的版本，缺省时默认为 1.4.15
    plugins: [
      'AMap.Scale', // 比例尺，显示当前地图中心的比例尺
      'AMap.ToolBar', // 工具条，控制地图的缩放、平移等
      'AMap.MapType', // 图层切换，用于几个常用图层切换显示
      'AMap.Geocoder', // 地理编码与逆地理编码服务，提供地址与坐标间的相互转换
      'AMap.Geolocation' // 定位，提供了获取用户当前准确位置、所在城市的方法
    ], // 需要使用的的插件列表，如比例尺'AMap.Scale'等
    AMapUI: {
      // 是否加载 AMapUI，缺省不加载
      version: '1.1', // AMapUI 缺省 1.1
      plugins: [] // 需要加载的 AMapUI ui插件
    },
    Loca: {
      // 是否加载 Loca， 缺省不加载
      version: '1.3.2' // Loca 版本，缺省 1.3.2
    }
  }

  AMapLoader.load(loadOption)
    .then((AMap) => {
      const map = new AMap.Map('a-map-container', {
        zoom: 17, // 级别
        center, // 中心点坐标
        viewMode: '3D' // 使用3D视图
      })
      // 实例化插件
      const toolbar = new AMap.ToolBar()
      map.addControl(toolbar)

      const scale = new AMap.Scale()
      map.addControl(scale)

      const mapType = new AMap.MapType()
      map.addControl(mapType)

      const geolocation = new AMap.Geolocation({
        // 是否使用高精度定位，默认：true
        enableHighAccuracy: true,
        // 设置定位超时时间，默认：无穷大
        timeout: 10000,
        // 定位按钮的停靠位置的偏移量，默认：Pixel(10, 20)
        buttonOffset: new AMap.Pixel(10, 20),
        //  定位成功后调整地图视野范围使定位位置及精度范围视野内可见，默认：false
        zoomToAccuracy: true,
        //  定位按钮的排放位置,  RB表示右下
        buttonPosition: 'RB'
      })
      map.addControl(geolocation)

      // 选择定位的点标记
      let chooseMarker: any = null

      /**
       * 设置点标记
       * @param {number} lng 经度
       * @param {number} lat 纬度
       * @param {string?} imgUrl 所用图标的地址
       * @returns 点标记
       */
      const setMarker = (
        lng: number,
        lat: number,
        imgUrl?: string
      ) => {
        // 如果有之前选择过的就移除，然后标记新的
        chooseMarker && removeMarker(chooseMarker)
        // 构造点标记
        const marker = new AMap.Marker({
          icon: imgUrl || 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
          position: [lng, lat],
          offset: new AMap.Pixel(0, 0), // 相对于基点的偏移位置
          anchor: 'bottom-center'
        })
        map.add(marker)
        map.setFitView()
        return marker
      }
      /**
       * 删除点标记
       * @param {*} marker 点标记
       */
      const removeMarker = (marker: any) => {
        map.remove(marker)
        map.setFitView()
      }

      /**
       * 将经纬度转为地址
       */
      const setAddress = (lnglat: number[]) => {
        updateLngNum(lnglat[0])
        updateLatNum(lnglat[1])
        emit('update:modelValue', lnglat)
        const geocoder = new AMap.Geocoder({
          radius: 1000,
          extensions: 'all'
        })
        geocoder.getAddress(
          lnglat,
          function (status: string, result: any) {
            if (
              status === 'complete' && result.info === 'OK'
            ) {
              const { regeocode } = result
              updateAddr(regeocode.formattedAddress)
              emit('onSeleted', {
                ...regeocode.addressComponent,
                formattedAddress: regeocode.formattedAddress
              })
            } else {
              console.log('获取位置出错！')
            }
          }
        )
      }

      /**
       * 设置地址和点标记
       * @param {Array} lng 经纬度
       */
      const setAddressAndMarker = setPositionByLnglat = (lnglat: number[]) => {
        chooseMarker = setMarker(lnglat[0], lnglat[1])
        setAddress(lnglat)
      }

      // 如果没有绑定经纬度，就标记当前定位
      if (!initLnglat) {
        // 标出当前位置
        geolocation.getCurrentPosition()
        AMap.event.addListener(
          geolocation,
          'complete',
          (data: any) => {
            // data是具体的定位信息
            const { lng, lat } = data.position
            setAddressAndMarker([lng, lat])
          }
        )
        AMap.event.addListener(
          geolocation,
          'error',
          (data: any) => {
            // 定位出错
            console.log(data)
          }
        )
      } else {
        setAddressAndMarker(initLnglat)
      }

      // 为地图注册click事件获取鼠标点击出的经纬度坐标
      map.on('click', (e: any) => {
        const lng = e.lnglat.getLng()
        const lat = e.lnglat.getLat()
        setAddressAndMarker([lng, lat])
      })
    })
    .catch((e) => {
      console.log(e)
    })
}

loadAMap(props.mapKey, initLnglat)

/**
 * 监听输入的关键词
 * @param {*} e 事件源
 */
const handleInput = (e: any) => {
  keywords = e.target.value.trim()
  // 如果有输入清除上一次开的定时器
  clearTimeout(timer)
  timer = setTimeout(() => {
    if (keywords) {
      const params = {
        key: props.mapKey,
        keywords,
        s: 'rsv3',
        platform: 'JS',
        logversion: '2.0',
        sdkversion: '1.4.16'
      }
      axios.get(searchUrl, { params }).then((res) => {
        if (res.status === 200) {
          tips.value = res.data.tips
          showSearchRes.value = true
        }
      })
    }
  }, 800)
}
/**
 * 选择检索出来的地址
 * @param {*} tip 地址信息
 */
const selectAddr = (tip: any) => {
  const { name, location } = tip
  const lnglat = location.split(',').map((num: string) => {
    return Number(num)
  })
  keywords = name
  showSearchRes.value = false
  setPositionByLnglat(lnglat)
}

</script>

<style scoped lang="less">
.a-map {
    .a-map-action {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 10px;
    }

    .a-map-search {
        position: relative;

        .a-map-search-input {
            width: 300px;
            height: 30px;
            padding: 4px 11px;
            color: rgba(0, 0, 0, 0.85);
            font-size: 14px;
            line-height: 1.5715;
            background-color: #fff;
            background-image: none;
            border: 1px solid #d9d9d9;
            border-radius: 2px;

            &:focus {
                border-color: #40a9ff;
                border-right-width: 1px !important;
                outline: 0;
                box-shadow: 0 0 0 2px rgb(24 144 255 / 20%);
            }
        }

        .a-map-tips {
            position: absolute;
            width: 100%;
            z-index: 520;
            list-style: none;
            padding: 10px 15px;
            margin: 0;
            background: #fff;
            border: 1px solid #d9d9d9;

            li {
                list-style: none;
                font-size: 14px;
                line-height: 2;
                cursor: pointer;

                &:hover {
                    color: #1890ff;
                }

                strong {
                    margin-right: 5px;
                }
            }
        }
    }

    #a-map-container {
        width: 100%;
        height: 50vh;
    }
}
</style>
